/******************************************************************************
 Copyright (c) 2022 - 2025 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

 REVISION:      $Revision: #3 $
******************************************************************************/
#include "csr_synergy.h"

#ifndef EXCLUDE_GATT_XPAN_SERVER_MODULE

#include "gatt_xpan_server.h"
#include "gatt_xpan_server_msg_handler.h"
#include "gatt_xpan_server_access.h"
#include "gatt_xpan_server_db.h"
#include "gatt_xpan_server_common.h"
#include "csr_list.h"
#include "gatt_lib.h"

#define VALID_SSID_LENGTH(x) (x >= GATT_XPAN_SERVER_MIN_SSID_SIZE && x <= GATT_XPAN_SERVER_MAX_SSID_SIZE)

ServiceHandle xpanServiceHandle;
static bool bssidChanged = FALSE;

static void *xpanPmemRealloc(void *ptr, CsrSize oldSize, CsrSize newSize)
{
    void *new;

    new = CsrPmemAlloc(newSize);

    if (ptr)
    {
        SynMemCpyS(new, newSize, ptr, oldSize);
        CsrPmemFree(ptr);
    }
    return new;
}

static void xpanListInit(CsrCmnListElm_t *elem)
{
    XpanClientDataElement *inst = (XpanClientDataElement *) elem;

    inst->clientData.cid = 0;
}

ServiceHandle GattXpanServerInit(
                     AppTask theAppTask,
                     uint16  startHandle,
                     uint16  endHandle,
                     const GattXpanInitData* initData)
{
    uint8 i = 0;
    GattXpanServerData *gattXpanServerInst = NULL;

    if (theAppTask == CSR_SCHED_QID_INVALID)
    {
        GATT_XPAN_SERVER_PANIC("GattXpanServerInit: Application Task NULL\n");
        return 0;
    }

    if (!initData || (startHandle > endHandle))
    {
        GATT_XPAN_SERVER_ERROR("GattXpanServerInit: appTask = %d initData = %p startHandle = %d endHandle = %d \n", theAppTask, initData, startHandle, endHandle);
        GATT_XPAN_SERVER_PANIC("GattXpanServerInit: Invalid parameters!\n");
        return 0;
    }

    /* Ensure audio location values are valid */
    for (i = 0; i < initData->macAddress.numMacAddresses; i++)
    {
        if (initData->macAddress.macAddressData[i].audioLocation > GATT_XPAN_SERVER_AUDIO_LOCATION_MAX)
        {
            GATT_XPAN_SERVER_ERROR("XPAN ERROR: Invalid MAC Address passed.");
            return 0;
        }
    }
    /* Ensure Num Devices Present value is valid */
    if (initData->numDevices < XPAN_SERVER_NUM_DEVICES_PRESENT_MIN)
    {
        GATT_XPAN_SERVER_ERROR("XPAN ERROR: Invalid Num Devices Present passed.");
        return 0;
    }
    /* Ensure Periodicity value is valid */
    if (initData->periodicity < XPAN_SERVER_PERIODICITY_MIN)
    {
        GATT_XPAN_SERVER_ERROR("XPAN ERROR: Invalid Periodicity passed.");
        return 0;
    }

    xpanServiceHandle = ServiceHandleNewInstance((void **)&gattXpanServerInst, sizeof(GattXpanServerData));

    if (gattXpanServerInst)
    {
        /* Set everything in instance data to 0 */
        CsrMemSet(gattXpanServerInst, 0, sizeof(GattXpanServerData));
        /* Store the Task function parameter.
         * All library messages need to be sent here */
        gattXpanServerInst->appTask = theAppTask;
        gattXpanServerInst->startHandle = startHandle;
        gattXpanServerInst->endHandle = endHandle;
        gattXpanServerInst->libTask = GATT_XPAN_SERVER_IFACEQUEUE;

        gattXpanServerInst->handoverMgr = NULL;

        /* Initialisation of the Xpan characteristics using data passed from application */
        gattXpanServerInst->data.l2capTcpPort = initData->l2capTcpPort;
        gattXpanServerInst->data.udpPort = initData->udpPort;
        gattXpanServerInst->data.macAddress.numMacAddresses = initData->macAddress.numMacAddresses;
        gattXpanServerInst->data.macAddress.macAddressData = (MacAddressData *)CsrPmemZalloc(sizeof(MacAddressData)*initData->macAddress.numMacAddresses);
        SynMemCpyS(gattXpanServerInst->data.macAddress.macAddressData,
                   sizeof(MacAddressData) * initData->macAddress.numMacAddresses,
                   initData->macAddress.macAddressData,
                   sizeof(MacAddressData) * initData->macAddress.numMacAddresses);
        gattXpanServerInst->data.numDevices = initData->numDevices;
        gattXpanServerInst->data.periodicity = initData->periodicity;
        SynMemCpyS(gattXpanServerInst->data.mDnsSrvUuid, GATT_XPAN_SERVER_MDNS_SRV_UUID_SIZE,
                   initData->mDnsSrvUuid, GATT_XPAN_SERVER_MDNS_SRV_UUID_SIZE);
        gattXpanServerInst->data.sapConnectionStatus = initData->sapConnectionStatus;
        gattXpanServerInst->data.twtStatus = initData->twtStatus;
        gattXpanServerInst->serviceHandle = xpanServiceHandle;

        /* Reset the client data memory */
        CsrCmnListInit(&(gattXpanServerInst->data.connectedClients), 0, xpanListInit, NULL);
        /* Register with the GATT */
        CsrBtGattRegisterReqSend(GATT_XPAN_SERVER_IFACEQUEUE, 0);

        return gattXpanServerInst->serviceHandle;
    }
    else
    {
        GATT_XPAN_SERVER_PANIC("XPAN: Memory allocation of XPAN Server instance failed!\n");

        return 0;
    }
}

status_t GattXpanServerAddConfig(
                  ServiceHandle               srvcHndl,
                  ConnectionId                cid,
                  GattXpanServerConfig *const config)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHndl);

    if(gattXpanServerInst)
    {
        /* Check config parameter:
        * If config is NULL, it indicates a default config should be used for the
        * peer device identified by the CID.
        * The default config is already set when the instance has been initialised.
        */
        if(config)
        {
            if(config->storedSsidClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->ipv4AddressClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->ipv6AddressClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->l2capTcpPortClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->udpPortClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->connectedDevicesClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->clearToSendCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->bearerPreferenceClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->requestedSapPowerStateCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->serverFeaturesCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->audioBearerSwitchResponseCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->numDevicesPresentClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->voiceBackChannelPeriodicityClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->requestWiFiScanResultsClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->roamingRequestResponseClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->macAddressesClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->wiFiScanResultsClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->preferredChannelsClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->wiFiChannelSwitchRequestClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->supportedWiFiChannelsClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->twtStatusClientCfg == GATT_XPAN_SERVER_CCC_INDICATE ||
                config->sapConnectionStatusClientCfg == GATT_XPAN_SERVER_CCC_INDICATE)

            {
                /* XPAN characteristics can be only notified */
                GATT_XPAN_SERVER_ERROR("Invalid Client Configuration Characteristic!\n");
                return CSR_BT_GATT_RESULT_INVALID_ATTRIBUTE_VALUE_RECEIVED;
            }
            else
            {
                /* Adding new client to front of list */
                XpanClientDataElement *client = XPAN_ADD_CLIENT(gattXpanServerInst->data.connectedClients);

                /* Store clients configuration in Server struct */
                client->clientData.clientCfg.storedSsidClientCfg = config->storedSsidClientCfg;
                client->clientData.clientCfg.ipv4AddressClientCfg = config->ipv4AddressClientCfg;
                client->clientData.clientCfg.ipv6AddressClientCfg = config->ipv6AddressClientCfg;
                client->clientData.clientCfg.l2capTcpPortClientCfg = config->l2capTcpPortClientCfg;
                client->clientData.clientCfg.udpPortClientCfg = config->udpPortClientCfg;
                client->clientData.clientCfg.connectedDevicesClientCfg = config->connectedDevicesClientCfg;
                client->clientData.clientCfg.clearToSendCfg = config->clearToSendCfg;
                client->clientData.clientCfg.bearerPreferenceClientCfg = config->bearerPreferenceClientCfg;
                client->clientData.clientCfg.macAddressesClientCfg = config->macAddressesClientCfg;
                client->clientData.clientCfg.requestedSapPowerStateCfg = config->requestedSapPowerStateCfg;
                client->clientData.clientCfg.serverFeaturesCfg = config->serverFeaturesCfg;
                client->clientData.clientCfg.audioBearerSwitchResponseCfg = config->audioBearerSwitchResponseCfg;
                client->clientData.clientCfg.numDevicesPresentClientCfg = config->numDevicesPresentClientCfg;
                client->clientData.clientCfg.voiceBackChannelPeriodicityClientCfg = config->voiceBackChannelPeriodicityClientCfg;
                client->clientData.clientCfg.requestWiFiScanResultsClientCfg = config->requestWiFiScanResultsClientCfg;
                client->clientData.clientCfg.roamingRequestResponseClientCfg = config->roamingRequestResponseClientCfg;
                client->clientData.clientCfg.wiFiScanResultsClientCfg = config->wiFiScanResultsClientCfg;
                client->clientData.clientCfg.preferredChannelsClientCfg = config->preferredChannelsClientCfg;
                client->clientData.clientCfg.wiFiChannelSwitchRequestClientCfg = config->wiFiChannelSwitchRequestClientCfg;
                client->clientData.clientCfg.supportedWiFiChannelsClientCfg = config->supportedWiFiChannelsClientCfg;
                client->clientData.clientCfg.twtStatusClientCfg = config->twtStatusClientCfg;
                client->clientData.clientCfg.sapConnectionStatusClientCfg = config->sapConnectionStatusClientCfg;
                client->clientData.cid = cid;
                CsrMemSet(&(client->clientData.twtConfig), 0, sizeof(TwtConfiguration));
                client->clientData.audioBearerStatus = XPAN_AUDIO_BEARER_SWITCH_RESPONSE_READY;
                client->clientData.clearToSend = XPAN_CTS_NOT_READY;

                if(gattXpanServerInst->data.connectedClients.last == NULL)
                {
                    gattXpanServerInst->data.connectedClients.last = gattXpanServerInst->data.connectedClients.first;
                }

                /* Notify new client of characteristic values */
                if(config->storedSsidClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint16 valueLength;
                    uint8 *value;

                    value = xpanServerComposeStoredSsidValue(&valueLength, gattXpanServerInst);

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_STORED_SSID,
                        valueLength,
                        value);
                }

                if(config->ipv4AddressClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint16 valueLength;
                    uint8 *value;

                    value = xpanServerComposeIpv4AddressValue(&valueLength, gattXpanServerInst);

                    if(value != NULL)
                    {
                        xpanServerSendCharacteristicChangedNotification(
                            gattXpanServerInst->gattId,
                            cid,
                            HANDLE_IPV4_ADDRESSES,
                            valueLength,
                            value);
                    }
                    else
                    {
                        xpanServerSendAccessRsp(
                            gattXpanServerInst->gattId,
                            cid,
                            HANDLE_IPV4_ADDRESSES,
                            CSR_BT_GATT_ACCESS_RES_UNLIKELY_ERROR,
                            0,
                            NULL);
                    }

                }

                if(config->ipv6AddressClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint16 valueLength;
                    uint8 *value;

                    value = xpanServerComposeIpv6AddressValue(&valueLength, gattXpanServerInst);

                    if(value != NULL)
                    {
                        xpanServerSendCharacteristicChangedNotification(
                            gattXpanServerInst->gattId,
                            cid,
                            HANDLE_IPV6_ADDRESSES,
                            valueLength,
                            value);
                    }
                    else
                    {
                        xpanServerSendAccessRsp(
                            gattXpanServerInst->gattId,
                            cid,
                            HANDLE_IPV6_ADDRESSES,
                            CSR_BT_GATT_ACCESS_RES_UNLIKELY_ERROR,
                            0,
                            NULL);
                    }
                }

                if(config->l2capTcpPortClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*GATT_XPAN_SERVER_L2CAP_TCP_PORT_SIZE);
                    value[0] = (uint8)gattXpanServerInst->data.l2capTcpPort;
                    value[1] = (uint8)(gattXpanServerInst->data.l2capTcpPort >> 8);

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_L2CAP_TCP_PORT,
                        GATT_XPAN_SERVER_L2CAP_TCP_PORT_SIZE,
                        value);
                }

                if(config->udpPortClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*GATT_XPAN_SERVER_UDP_PORT_SIZE);
                    value[0] = (uint8)gattXpanServerInst->data.udpPort;
                    value[1] = (uint8)(gattXpanServerInst->data.udpPort >> 8);

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_UDP_PORT,
                        GATT_XPAN_SERVER_UDP_PORT_SIZE,
                        value);

                }

                if(config->connectedDevicesClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint16 valuelength = (XPAN_SERVER_CONNECTED_DEVICES_DATA_SIZE*gattXpanServerInst->data.connectedDevices.numDevices) + 1;
                    uint8 *value = NULL;
                    value = xpanServerComposeConnectedDevicesValue(&valuelength, gattXpanServerInst);

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_CONNECTED_DEVICES,
                        valuelength,
                        value);
                }

                if(config->bearerPreferenceClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint16 valueLength;
                    uint8  *value = xpanServerComposeBearerPreferenceValue(&valueLength, gattXpanServerInst);

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_BEARER_PREFERENCE,
                        GATT_XPAN_SERVER_BEARER_PREFERENCE_SIZE,
                        value);
                }

                if(config->requestedSapPowerStateCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*GATT_XPAN_SERVER_REQUESTED_SAP_POWER_STATE_SIZE);

                    value[0] = gattXpanServerInst->data.requestedSapPowerState;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_REQUESTED_SAP_POWER_STATE,
                        GATT_XPAN_SERVER_REQUESTED_SAP_POWER_STATE_SIZE,
                        value);
                }

                if(config->serverFeaturesCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*XPAN_SERVER_SERVER_FEATURES_SIZE);

                    SynMemCpyS(value, XPAN_SERVER_SERVER_FEATURES_SIZE, gattXpanServerInst->data.features, XPAN_SERVER_SERVER_FEATURES_SIZE);

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_SERVER_FEATURES,
                        XPAN_SERVER_SERVER_FEATURES_SIZE,
                        value);
                }

                if(config->audioBearerSwitchResponseCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*XPAN_SERVER_AUDIO_BEARER_SWITCH_RESPONSE_SIZE);

                    value[0] = client->clientData.audioBearerStatus;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_AUDIO_BEARER_SWITCH_RESPONSE,
                        XPAN_SERVER_AUDIO_BEARER_SWITCH_RESPONSE_SIZE,
                        value);
                }

                if(config->numDevicesPresentClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*XPAN_SERVER_NUM_DEVICES_PRESENT_SIZE);

                    value[0] = gattXpanServerInst->data.numDevices;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_NUM_DEVICES_PRESENT,
                        XPAN_SERVER_NUM_DEVICES_PRESENT_SIZE,
                        value);
                }

                if(config->voiceBackChannelPeriodicityClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*XPAN_SERVER_VOICE_CHANNEL_PERIODICITY_SIZE);

                    value[0] = gattXpanServerInst->data.periodicity;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_VOICE_BACK_CHANNEL_PERIODICITY,
                        XPAN_SERVER_VOICE_CHANNEL_PERIODICITY_SIZE,
                        value);
                }

                if (config->requestWiFiScanResultsClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_REQUEST_WIFI_SCAN_RESULTS,
                        XPAN_SERVER_REQUEST_WIFI_SCAN_RESULTS_SIZE,
                        NULL);
                }

                if (config->macAddressesClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint16 valueLength = 0;
                    uint8* value = xpanServerComposeMacAddressValue(&valueLength, gattXpanServerInst);
                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_MAC_ADDRESSES,
                        valueLength,
                        value);
                }

                if (config->twtStatusClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*XPAN_SERVER_TWT_STATUS_SIZE);

                    value[0] = gattXpanServerInst->data.twtStatus;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_TWT_STATUS,
                        XPAN_SERVER_TWT_STATUS_SIZE,
                        value);
                }

                if (config->sapConnectionStatusClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*XPAN_SERVER_SAP_CONNECTION_STATUS_SIZE);

                    value[0] = gattXpanServerInst->data.sapConnectionStatus;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_SAP_CONNECTION_STATUS,
                        XPAN_SERVER_SAP_CONNECTION_STATUS_SIZE,
                        value);
                }

                return CSR_BT_GATT_RESULT_SUCCESS;
            }
        }
        else
        {
            XpanClientDataElement *client = XPAN_ADD_CLIENT(gattXpanServerInst->data.connectedClients);
            client->clientData.cid = cid;
            CsrMemSet(&client->clientData.clientCfg, 0, sizeof(GattXpanServerConfig));
            return CSR_BT_GATT_RESULT_SUCCESS;
        }
    }

    return CSR_BT_GATT_RESULT_UNACCEPTABLE_PARAMETER;
}

GattXpanServerConfig* GattXpanServerRemoveConfig(
                        ServiceHandle srvcHndl,
                        ConnectionId  cid)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHndl);
    GattXpanServerConfig *config = NULL;

    if(gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);
        if (client)
        {
            config = (GattXpanServerConfig *)CsrPmemZalloc(sizeof(GattXpanServerConfig));
            SynMemCpyS(config, sizeof(GattXpanServerConfig), &(client->clientData.clientCfg), sizeof(GattXpanServerConfig));
            XPAN_REMOVE_CLIENT(gattXpanServerInst->data.connectedClients, client);
        }
    }

    return config;
}

GattXpanServerConfig* GattXpanServerGetConfig(
                        ServiceHandle srvcHndl,
                        ConnectionId  cid)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHndl);
    GattXpanServerConfig *config = NULL;

    if(gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);
        if (client)
        {
            config = (GattXpanServerConfig *)CsrPmemZalloc(sizeof(GattXpanServerConfig));
            SynMemCpyS(config, sizeof(GattXpanServerConfig), &(client->clientData.clientCfg), sizeof(GattXpanServerConfig));
        }
    }

    return config;
}

/******************************************************************************/
/* XPAN server init Synergy Scheduler Task */

void GattXpanServerSynergyInit(void** gash)
{
    *gash = &xpanServiceHandle;
    GATT_XPAN_SERVER_INFO("XPAN: GattXpanServerSynergyInit\n\n");
}

/******************************************************************************/

/******************************************************************************/
/* Synergy Task De-Initalization */
#ifdef ENABLE_SHUTDOWN
void gattXpanServerTaskDeinit(void **gash)
{
    ServiceHandle srvHndl = *((ServiceHandle*)*gash);

    if (srvHndl != INVALID_PROFILE_HANDLE)
    {
        XpanServerHandoverMgrCleanup(srvHndl);
        if (ServiceHandleFreeInstanceData(srvHndl))
        {
            GATT_XPAN_SERVER_DEBUG_INFO(("\n XPAN: gattXpanServerTaskDeinit Free service handle \n"));
            qssServerServiceHandle = INVALID_PROFILE_HANDLE;
        }
        else
        {
            GATT_XPAN_SERVER_DEBUG_PANIC(("\n XPAN: Unable to free the XPAN service instance \n"));
        }
    }
    else
    {
        GATT_XPAN_SERVER_ERROR(("\n XPAN: Invalid service handle value \n"));
    }
}
#endif

/******************************************************************************/

void xpanServerHandleChangeStoredSsid(GattXpanServerData *gattXpanServerInst)
{
    XpanClientDataElement *client = (XpanClientDataElement*)gattXpanServerInst->data.connectedClients.first;
    uint16 valueLength;
    uint8 *value = NULL;

    if(client)
    {
        for (; client; client = client->next)
        {
            if(client->clientData.clientCfg.storedSsidClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                value = xpanServerComposeStoredSsidValue(&valueLength, gattXpanServerInst);

                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    client->clientData.cid,
                    HANDLE_STORED_SSID,
                    valueLength,
                    value);
            }
        }
    }
}

void xpanServerHandleChangeL2capTcpPort(GattXpanServerData *gattXpanServerInst)
{
    XpanClientDataElement *client = (XpanClientDataElement*)gattXpanServerInst->data.connectedClients.first;
    uint8 *value = NULL;

    if(client)
    {
        for (; client; client = client->next)
        {
            if(client->clientData.clientCfg.l2capTcpPortClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                value = (uint8 *)CsrPmemAlloc(sizeof(uint8) * GATT_XPAN_SERVER_L2CAP_TCP_PORT_SIZE);

                value[0] = (uint8)gattXpanServerInst->data.l2capTcpPort;
                value[1] = (uint8)(gattXpanServerInst->data.l2capTcpPort >> 8);

                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    client->clientData.cid,
                    HANDLE_L2CAP_TCP_PORT,
                    GATT_XPAN_SERVER_L2CAP_TCP_PORT_SIZE,
                    value);
            }
        }
    }
}

void xpanServerHandleChangeUdpPort(GattXpanServerData *gattXpanServerInst)
{
    XpanClientDataElement *client = (XpanClientDataElement*)gattXpanServerInst->data.connectedClients.first;
    uint8 *value = NULL;

    if(client)
    {
        for (; client; client = client->next)
        {
            if(client->clientData.clientCfg.udpPortClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*GATT_XPAN_SERVER_UDP_PORT_SIZE);

                value[0] = (uint8)gattXpanServerInst->data.udpPort;
                value[1] = (uint8)(gattXpanServerInst->data.udpPort >> 8);

                xpanServerSendCharacteristicChangedNotification(
                                gattXpanServerInst->gattId,
                                client->clientData.cid,
                                HANDLE_UDP_PORT,
                                GATT_XPAN_SERVER_UDP_PORT_SIZE,
                                value);
            }
        }
    }
}

void xpanServerHandleChangeTwtConfiguration(GattXpanServerData *gattXpanServerInst, ConnectionId cid)
{
    uint16 valueLength;
    uint8 *value = NULL;

    value = xpanServerComposeTwtConfigurationValue(&valueLength, gattXpanServerInst, cid);

    if(value != NULL)
    {
        xpanServerSendCharacteristicChangedNotification(
                            gattXpanServerInst->gattId,
                            cid,
                            HANDLE_TWT_CONFIGURATION,
                            valueLength,
                            value);
    }
    else
    {
        xpanServerSendCharacteristicChangedNotification(
                            gattXpanServerInst->gattId,
                            cid,
                            HANDLE_TWT_CONFIGURATION,
                            0,
                            NULL);
    }

}

void xpanServerHandleChangeClearToSend(GattXpanServerData *gattXpanServerInst, ConnectionId cid)
{
    XpanClientDataElement *client = xpanFindClient(&gattXpanServerInst->data.connectedClients, cid);
    uint8 *value = NULL;

    if (client)
    {
        value = (uint8 *)CsrPmemAlloc(sizeof(uint8) * GATT_XPAN_SERVER_CLEAR_TO_SEND_SIZE);
        value[0] = client->clientData.clearToSend;

        xpanServerSendCharacteristicChangedNotification(
            gattXpanServerInst->gattId,
            cid,
            HANDLE_CLEAR_TO_SEND,
            GATT_XPAN_SERVER_CLEAR_TO_SEND_SIZE,
            value);
    }
}

void xpanServerHandleChangeIpv4Address(GattXpanServerData *inst)
{
    uint16 valueLength;
    uint8 *value = NULL;
    XpanClientDataElement *client = NULL;

    for (client = (XpanClientDataElement *)inst->data.connectedClients.first; client; client = client->next)
    {
        if(client->clientData.clientCfg.ipv4AddressClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
        {
            value = xpanServerComposeIpv4AddressValue(&valueLength, inst);

            xpanServerSendCharacteristicChangedNotification(
                            inst->gattId,
                            client->clientData.cid,
                            HANDLE_IPV4_ADDRESSES,
                            valueLength,
                            value);
        }
    }
}

void xpanServerHandleChangeIpv6Address(GattXpanServerData *inst)
{
    uint16 valueLength;
    uint8 *value = NULL;
    XpanClientDataElement *client = NULL;

    for (client = (XpanClientDataElement *)inst->data.connectedClients.first; client; client = client->next)
    {
        if(client->clientData.clientCfg.ipv6AddressClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
        {
            value = xpanServerComposeIpv6AddressValue(&valueLength, inst);

            xpanServerSendCharacteristicChangedNotification(
                            inst->gattId,
                            client->clientData.cid,
                            HANDLE_IPV6_ADDRESSES,
                            valueLength,
                            value);
        }
    }
}

bool GattXpanServerSetStoredSsids(ServiceHandle srvcHandle, const StoredSsid *storedSsid)
{
    uint8 i;
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    /* Validate data */
    if((storedSsid->numSsid == 0 && storedSsid->ssid != NULL) || (storedSsid->numSsid != 0 && storedSsid->ssid == NULL))
        return FALSE;

    for(i = 0; i < storedSsid->numSsid; i++)
    {
        if(!VALID_SSID_LENGTH(storedSsid->ssid[i].ssidLength) || !storedSsid->ssid[i].ssid)
        {
            return FALSE;
        }
    }

    if(gattXpanServerInst && !xpanCompareStoredSsids(gattXpanServerInst, storedSsid))
    {
        /* Free memory for existing list to replace with new list. */
        for(i = 0; i < gattXpanServerInst->data.storedSsid.numSsid; i++)
        {
            pfree(gattXpanServerInst->data.storedSsid.ssid[i].ssid);
        }
        gattXpanServerInst->data.storedSsid.ssid = xpanPmemRealloc(gattXpanServerInst->data.storedSsid.ssid,
                                                                    gattXpanServerInst->data.storedSsid.numSsid*sizeof(SsidData),
                                                                    storedSsid->numSsid*sizeof(SsidData));
        gattXpanServerInst->data.storedSsid.numSsid = storedSsid->numSsid;
        for (i = 0; i < storedSsid->numSsid; i++)
        {
            gattXpanServerInst->data.storedSsid.ssid[i].ssidLength = storedSsid->ssid[i].ssidLength;
            gattXpanServerInst->data.storedSsid.ssid[i].ssid = (uint8 *)CsrPmemZalloc(sizeof(uint8)*storedSsid->ssid[i].ssidLength);
            if(gattXpanServerInst->data.storedSsid.ssid[i].ssid)
            {
                SynMemCpyS(gattXpanServerInst->data.storedSsid.ssid[i].ssid,
                           sizeof(uint8)*storedSsid->ssid[i].ssidLength,
                           storedSsid->ssid[i].ssid,
                           sizeof(uint8)*storedSsid->ssid[i].ssidLength);
            }
            else
            {
                GATT_XPAN_SERVER_PANIC("XPAN SERVER: Unable to allocate memory to Stored ssid.");
            }
        }

        xpanServerHandleChangeStoredSsid(gattXpanServerInst);
        return TRUE;
    }

    return FALSE;
}

bool GattXpanServerSetL2capTcpPort(ServiceHandle srvcHandle, uint16 tcpPort)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if (gattXpanServerInst && (tcpPort != gattXpanServerInst->data.l2capTcpPort))
    {
        gattXpanServerInst->data.l2capTcpPort = tcpPort;

        xpanServerHandleChangeL2capTcpPort(gattXpanServerInst);

        return TRUE;
    }

    return FALSE;
}

bool GattXpanServerSetUdpPort(ServiceHandle srvcHandle, uint16 udpPort)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if (gattXpanServerInst && (udpPort != gattXpanServerInst->data.udpPort))
    {
        gattXpanServerInst->data.udpPort = udpPort;

        xpanServerHandleChangeUdpPort(gattXpanServerInst);

        return TRUE;
    }

    return FALSE;
}

bool GattXpanServerSetmDnsSrvUuid(ServiceHandle srvcHandle, uint8 *uuid)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if (gattXpanServerInst && (CsrMemCmp(uuid, gattXpanServerInst->data.mDnsSrvUuid, GATT_XPAN_SERVER_MDNS_SRV_UUID_SIZE) != 0))
    {
        SynMemCpyS(gattXpanServerInst->data.mDnsSrvUuid, GATT_XPAN_SERVER_MDNS_SRV_UUID_SIZE, uuid, GATT_XPAN_SERVER_MDNS_SRV_UUID_SIZE);

        return TRUE;
    }

    return FALSE;
}

bool GattXpanServerSetTwtConfiguration(ServiceHandle srvcHandle, ConnectionId cid, TwtConfiguration *configuration)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if (gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&gattXpanServerInst->data.connectedClients, cid);

        if(client && (configuration->setupId != 0) && XPAN_SERVER_CHECK_BITS_VALID(configuration->bitfield, XPAN_SERVER_TWT_CONFIG_BITFIELD_VALID_BITS))
        {
            if(client->clientData.twtConfig.bitfield != configuration->bitfield ||
                (client->clientData.twtConfig.desiredWakeDuration != configuration->desiredWakeDuration) ||
                (client->clientData.twtConfig.desiredWakeInterval != configuration->desiredWakeInterval) ||
                (CsrMemCmp(client->clientData.twtConfig.desiredWakeTsfLeft, configuration->desiredWakeTsfLeft, 8) != 0) ||
                (client->clientData.twtConfig.desiredWakeTsfRightOffset != configuration->desiredWakeTsfRightOffset) ||
                (client->clientData.twtConfig.setupId != configuration->setupId))
            {
                SynMemCpyS(&(client->clientData.twtConfig), sizeof(TwtConfiguration), configuration, sizeof(TwtConfiguration));

                xpanServerHandleChangeTwtConfiguration(gattXpanServerInst,
                                                        client->clientData.cid);

                return TRUE;
            }
        }
    }

    return FALSE;
}

bool GattXpanServerSetClearToSend(ServiceHandle srvcHandle, ConnectionId cid, ClearToSend clearToSend)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);
    XpanClientDataElement *client = gattXpanServerInst ? xpanFindClient(&gattXpanServerInst->data.connectedClients, cid) : NULL;

    if(client)
    {
        if((client->clientData.clearToSend != clearToSend) && (clearToSend <= XPAN_CTS_READY) )
        {
            client->clientData.clearToSend = clearToSend;

            xpanServerHandleChangeClearToSend(gattXpanServerInst,
                                                cid);

            return TRUE;
        }
    }

    return FALSE;
}

bool GattXpanServerAddIpv4AddressSsid(ServiceHandle srvcHandle, uint8 *ssid,
                                        uint8 ssidLen, uint8 *bssid)
{
    uint8 i;
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);
    Ipv4Ssid *currentSsid = NULL;

    if (gattXpanServerInst)
    {
        if(ssidLen >= GATT_XPAN_SERVER_MIN_SSID_SIZE && ssidLen <= GATT_XPAN_SERVER_MAX_SSID_SIZE)
        {
            /* Search ssid list for matching ssid */
            for(i = 0; i < gattXpanServerInst->data.ipv4Address.numSsids; i++)
            {
                if (ssidLen == gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssidLength &&
                    !CsrMemCmp(ssid, gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssid,
                                gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssidLength))
                {
                    currentSsid = &gattXpanServerInst->data.ipv4Address.ipv4Ssids[i];
                    break;
                }
            }

            if(currentSsid)
            {
                /* Matching ssid found */
                if (CsrMemCmp(currentSsid->bssid, bssid, GATT_XPAN_SERVER_BSSID_SIZE))
                {
                    /* BSSID has been changed */
                    SynMemCpyS(currentSsid->bssid, GATT_XPAN_SERVER_BSSID_SIZE, bssid, GATT_XPAN_SERVER_BSSID_SIZE);
                }
                else
                {
                    /* Ssid exists and there is no change to tuple */
                    return FALSE;
                }
            }
            else
            {
                uint8 ssidCount = gattXpanServerInst->data.ipv4Address.numSsids;
                /* New ssid is to be added to the list */
                gattXpanServerInst->data.ipv4Address.ipv4Ssids = (Ipv4Ssid *)xpanPmemRealloc(gattXpanServerInst->data.ipv4Address.ipv4Ssids,
                                                                                            ssidCount*sizeof(Ipv4Ssid),
                                                                                            (ssidCount+1)*sizeof(Ipv4Ssid));
                /* Add the data for new ssid at the end and account for extra ssid in count */
                gattXpanServerInst->data.ipv4Address.numSsids++;
                gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidCount].ipv4Ssid.ssidLength  = ssidLen;
                gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidCount].ipv4Ssid.ssid = (uint8 *)CsrPmemZalloc(sizeof(uint8)*ssidLen);
                SynMemCpyS(gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidCount].ipv4Ssid.ssid,
                            ssidLen, ssid, ssidLen);
                SynMemCpyS(gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidCount].bssid,
                        GATT_XPAN_SERVER_BSSID_SIZE, bssid, GATT_XPAN_SERVER_BSSID_SIZE);
                gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidCount].numIpv4Addresses =  0;
                gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidCount].ipv4Addresses = NULL;
            }

            bssidChanged = TRUE;
            return TRUE;
        }
    }
    return FALSE;
}

bool GattXpanServerRemoveIpv4AddressSsid(ServiceHandle srvcHandle, uint8 *ssid,
                                         uint8 ssidLen)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);
    bool result = FALSE;

    if(gattXpanServerInst)
    {
        uint8 ssidIdx;
        bool moveToLowerIndex = FALSE;
        uint8 numSsids = gattXpanServerInst->data.ipv4Address.numSsids;

        for (ssidIdx = 0; ssidIdx < numSsids; ssidIdx++)
        {
            if (moveToLowerIndex)
            {
                gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx - 1] = gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx];
            }
            else
            {
                if (ssidLen == gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx].ipv4Ssid.ssidLength &&
                        !CsrMemCmp(ssid, gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx].ipv4Ssid.ssid,
                                    gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx].ipv4Ssid.ssidLength))
                {
                    /* Free associated IP Addresses */
                    pfree(gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx].ipv4Addresses);
                    gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx].ipv4Addresses = NULL;

                    /* Free associated SSID octets */
                    pfree(gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx].ipv4Ssid.ssid);
                    gattXpanServerInst->data.ipv4Address.ipv4Ssids[ssidIdx].ipv4Ssid.ssid = NULL;

                    moveToLowerIndex = TRUE;
                    gattXpanServerInst->data.ipv4Address.numSsids--;
                    result = TRUE;
                }
            }
        }

        if (moveToLowerIndex)
        {
            xpanServerHandleChangeIpv4Address(gattXpanServerInst);
            gattXpanServerInst->data.ipv4Address.ipv4Ssids = (Ipv4Ssid*)xpanPmemRealloc(gattXpanServerInst->data.ipv4Address.ipv4Ssids,
                                                                                         numSsids * sizeof(Ipv4Ssid),
                                                                                         gattXpanServerInst->data.ipv4Address.numSsids * sizeof(Ipv4Ssid));
        }

        if (gattXpanServerInst->data.ipv4Address.numSsids == 0)
        {
            pfree(gattXpanServerInst->data.ipv4Address.ipv4Ssids);
            gattXpanServerInst->data.ipv4Address.ipv4Ssids = NULL;
        }
    }
    return result;
}

bool GattXpanServerAddIpv4AddressV2(ServiceHandle srvcHandle,
                                    uint8 *ssid,
                                    uint8 ssidLen,
                                    uint8 *ipv4Address,
                                    GattXpanAudioLoc audioLocation,
                                    GattXpanRole role,
                                    uint8* macAddress,
                                    GattXpanIpAddressStatus status,
                                    bool notify)
{
    uint8 i, j;
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        if(ssidLen >= GATT_XPAN_SERVER_MIN_SSID_SIZE && ssidLen <= GATT_XPAN_SERVER_MAX_SSID_SIZE &&
           audioLocation <= GATT_XPAN_SERVER_AUDIO_LOCATION_MAX && role <= XPAN_ROLE_SECONDARY && status <= XPAN_STATUS_INTERNAL_ERROR)
         {
            /* Need to find the ipv4Address in list */
            for(i = 0; i < gattXpanServerInst->data.ipv4Address.numSsids; i++)
            {
                if (ssidLen == gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssidLength &&
                    CsrMemCmp(ssid, gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssid,
                                gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssidLength) == 0)
                {
                    uint8 ipv4AddressCount = gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].numIpv4Addresses;

                    /* Determine whether Ipv4 Address exists */
                    for(j = 0; j < ipv4AddressCount; j++)
                    {
                        if(CsrMemCmp(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].ipv4Address,
                                        ipv4Address, XPAN_SERVER_IPV4_ADDRESS_SIZE) == 0)
                        {
                            /* Update values if an attribute is to be changed */
                            if((gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].audioLocation != audioLocation) ||
                                gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].role != role ||
                                gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].status != status ||
                                CsrMemCmp(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].macAddress,
                                          macAddress, GATT_XPAN_SERVER_MAC_ADDRESS_SIZE) ||
                                bssidChanged)
                            {
                                gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].audioLocation = audioLocation;
                                gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].role = role;
                                gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].status = status;
                                SynMemCpyS(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].macAddress,
                                           GATT_XPAN_SERVER_MAC_ADDRESS_SIZE, macAddress, GATT_XPAN_SERVER_MAC_ADDRESS_SIZE);
                                bssidChanged = FALSE;

                                if (notify)
                                {
                                    /* Notify the change in existing address to the client */
                                    xpanServerHandleChangeIpv4Address(gattXpanServerInst);
                                }

                                return TRUE;
                            }
                            else
                            {
                                /* Values are the same */
                                return FALSE;
                            }
                        }
                    }
                    /* Ipv4 address does not exist and needs to be added.
                     * Allocate more memory for pointer array. */
                    gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses = (Ipv4AddressData*) xpanPmemRealloc(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses,
                                                                                                                        ipv4AddressCount*sizeof(Ipv4AddressData),
                                                                                                                        (ipv4AddressCount+1)*sizeof(Ipv4AddressData));
                    SynMemCpyS(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[ipv4AddressCount].ipv4Address,
                               XPAN_SERVER_IPV4_ADDRESS_SIZE, ipv4Address, XPAN_SERVER_IPV4_ADDRESS_SIZE);
                    gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[ipv4AddressCount].audioLocation = audioLocation;
                    gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[ipv4AddressCount].role = role;
                    SynMemCpyS(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[ipv4AddressCount].macAddress,
                               GATT_XPAN_SERVER_MAC_ADDRESS_SIZE, macAddress, GATT_XPAN_SERVER_MAC_ADDRESS_SIZE);
                    gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[ipv4AddressCount].status = status;
                    gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].numIpv4Addresses++;

                    /* Changing the characteristic does not always notify the clients.
                     * Subscribed clients for notification of the ipv4 address characteristic are notified
                     * in the following instances:
                     * - New address added has a role of primary.
                     * - New address has a role of secondary, successfully obtained the IP Address
                     *   and primary is already in the list.
                     */
                    if (role == XPAN_ROLE_PRIMARY)
                    {
                        if (notify)
                        {
                            /* Notify the change in existing address to the client */
                            xpanServerHandleChangeIpv4Address(gattXpanServerInst);
                        }
                    }
                    else if (gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].numIpv4Addresses > 1 &&
                            status == XPAN_STATUS_SUCCESS)
                    {
                        uint8 k;
                        /* Need to determine whether primary exists in the list of IP Addresses */
                        for(k = 0; k < ipv4AddressCount; k++)
                        {
                            /* if the the primary exsits and was successful then we notify */
                            if(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[k].role == XPAN_ROLE_PRIMARY &&
                                gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[k].status == XPAN_STATUS_SUCCESS)
                            {
                                if (notify)
                                {
                                    /* Notify the change in existing address to the client */
                                    xpanServerHandleChangeIpv4Address(gattXpanServerInst);
                                }
                                break;
                            }
                        }
                    }

                    return TRUE;
                }

                GATT_XPAN_SERVER_ERROR("XPAN: Invalid parameter, ssid does not exist.");
            }

            GATT_XPAN_SERVER_ERROR("XPAN: Invalid parameter.");
        }
    }
    /* Nothing updated */
    return FALSE;
}

bool GattXpanServerAddIpv4Address(ServiceHandle srvcHandle,
                                  uint8 *ssid,
                                  uint8 ssidLen,
                                  uint8 *ipv4Address,
                                  GattXpanAudioLoc audioLocation,
                                  GattXpanRole role,
                                  uint8* macAddress,
                                  GattXpanIpAddressStatus status)
{
    return GattXpanServerAddIpv4AddressV2(srvcHandle, ssid, ssidLen, ipv4Address, audioLocation, role, macAddress, status, TRUE);
}

bool GattXpanServerRemoveIpv4Address(ServiceHandle srvcHandle, uint8 *ssid,
                                    uint8 ssidLen, uint8 *ipv4Address)
{
    uint8 i, j;
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        /* Need to find the ipv4Address in list */
        for(i = 0; i < gattXpanServerInst->data.ipv4Address.numSsids; i++)
        {
            if (ssidLen == gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssidLength &&
                !CsrMemCmp(ssid, gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssid,
                            gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssidLength))
            {
                uint8 ipv4AddressCount = gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].numIpv4Addresses;

                /* Determine whether Ipv4 Address exists */
                for(j = 0; j < ipv4AddressCount; j++)
                {
                    if(!CsrMemCmp(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j].ipv4Address,
                                    ipv4Address, XPAN_SERVER_IPV4_ADDRESS_SIZE))
                    {
                        if (j != ipv4AddressCount-1)
                        {
                            gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[j] = gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses[ipv4AddressCount-1];
                        }
                        gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses = (Ipv4AddressData *)xpanPmemRealloc(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses,
                                                                                                                            ipv4AddressCount*sizeof(Ipv4AddressData),
                                                                                                                            (ipv4AddressCount-1)*sizeof(Ipv4AddressData));
                        gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].numIpv4Addresses--;

                        xpanServerHandleChangeIpv4Address(gattXpanServerInst);

                        return TRUE;
                    }
                }
            }
        }

        GATT_XPAN_SERVER_ERROR("XPAN: Invalid parameter, ipv4 address does not exist.");
    }

    return FALSE;
}

bool GattXpanServerAddIpv6AddressSsid(ServiceHandle srvcHandle, uint8 *ssid,
                                        uint8 ssidLen, uint8 *bssid)
{
    uint8 i;
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);
    Ipv6Ssid *currentSsid = NULL;

    if(ssidLen >= GATT_XPAN_SERVER_MIN_SSID_SIZE && ssidLen <= GATT_XPAN_SERVER_MAX_SSID_SIZE)
    {

        if (gattXpanServerInst)
        {
            /* Search ssid list for matching ssid */
            for(i = 0; i < gattXpanServerInst->data.ipv6Address.numSsids; i++)
            {
                if (ssidLen == gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssidLength &&
                    !CsrMemCmp(ssid, gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssid,
                                gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssidLength))
                {
                    currentSsid = &gattXpanServerInst->data.ipv6Address.ipv6Ssids[i];
                }
            }

            if(currentSsid)
            {
                /* Matching ssid found */
                if (CsrMemCmp(currentSsid->bssid, bssid, GATT_XPAN_SERVER_BSSID_SIZE))
                {
                    /* BSSID has been changed */
                    SynMemCpyS(currentSsid->bssid, GATT_XPAN_SERVER_BSSID_SIZE, bssid, GATT_XPAN_SERVER_BSSID_SIZE);
                }
                else
                {
                    /* Ssid exists and there is no change to tuple */
                    return FALSE;
                }
            }
            else
            {
                uint8 ssidCount = gattXpanServerInst->data.ipv6Address.numSsids;
                /* New ssid is to be added to the list */
                gattXpanServerInst->data.ipv6Address.ipv6Ssids = (Ipv6Ssid *)xpanPmemRealloc(gattXpanServerInst->data.ipv6Address.ipv6Ssids,
                                                                                            ssidCount*sizeof(Ipv6Ssid),
                                                                                            (ssidCount+1)*sizeof(Ipv6Ssid));
                /* Add the data for new ssid at the end and account for extra ssid in count */
                gattXpanServerInst->data.ipv6Address.numSsids++;
                gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidCount].ipv6Ssid.ssidLength  = ssidLen;
                gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidCount].ipv6Ssid.ssid = (uint8 *)CsrPmemZalloc(sizeof(uint8)*ssidLen);
                SynMemCpyS(gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidCount].ipv6Ssid.ssid,
                            ssidLen, ssid, ssidLen);
                SynMemCpyS(gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidCount].bssid,
                        GATT_XPAN_SERVER_BSSID_SIZE, bssid, GATT_XPAN_SERVER_BSSID_SIZE);
                gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidCount].numIpv6Addresses =  0;
                gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidCount].ipv6Addresses = NULL;
            }

            bssidChanged = TRUE;
            return TRUE;
        }
    }

    return FALSE;
}

bool GattXpanServerRemoveIpv6AddressSsid(ServiceHandle srvcHandle, uint8 *ssid,
                                         uint8 ssidLen)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);
    bool result = FALSE;

    if(gattXpanServerInst)
    {
        uint8 ssidIdx;
        bool moveToLowerIndex = FALSE;
        uint8 numSsids = gattXpanServerInst->data.ipv6Address.numSsids;

        for (ssidIdx = 0; ssidIdx < numSsids; ssidIdx++)
        {
            if (moveToLowerIndex)
            {
                gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx - 1] = gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx];
            }
            else
            {
                if (ssidLen == gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx].ipv6Ssid.ssidLength &&
                        !CsrMemCmp(ssid, gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx].ipv6Ssid.ssid,
                                    gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx].ipv6Ssid.ssidLength))
                {
                    /* Free associated IP Addresses */
                    pfree(gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx].ipv6Addresses);
                    gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx].ipv6Addresses = NULL;

                    /* Free associated SSID octets */
                    pfree(gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx].ipv6Ssid.ssid);
                    gattXpanServerInst->data.ipv6Address.ipv6Ssids[ssidIdx].ipv6Ssid.ssid = NULL;

                    moveToLowerIndex = TRUE;
                    gattXpanServerInst->data.ipv6Address.numSsids--;
                    result = TRUE;
                }
            }
        }

        if (moveToLowerIndex)
        {
            xpanServerHandleChangeIpv6Address(gattXpanServerInst);
            gattXpanServerInst->data.ipv6Address.ipv6Ssids = (Ipv6Ssid*)xpanPmemRealloc(gattXpanServerInst->data.ipv6Address.ipv6Ssids,
                                                                                         numSsids * sizeof(Ipv6Ssid),
                                                                                         gattXpanServerInst->data.ipv6Address.numSsids * sizeof(Ipv6Ssid));
        }

        if (gattXpanServerInst->data.ipv6Address.numSsids == 0)
        {
            pfree(gattXpanServerInst->data.ipv6Address.ipv6Ssids);
            gattXpanServerInst->data.ipv6Address.ipv6Ssids = NULL;
        }
    }
    return result;
}

bool GattXpanServerAddIpv6AddressV2(ServiceHandle srvcHandle, uint8 *ssid,
                                    uint8 ssidLen, uint8 *ipv6Address,
                                    GattXpanAudioLoc audioLocation, GattXpanRole role,
                                    uint8* macAddress,
                                    GattXpanIpAddressStatus status,
                                    bool notify)
{
    uint8 i, j;
   GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

   if(gattXpanServerInst)
   {
       if(ssidLen >= GATT_XPAN_SERVER_MIN_SSID_SIZE && ssidLen <= GATT_XPAN_SERVER_MAX_SSID_SIZE &&
          audioLocation <= GATT_XPAN_SERVER_AUDIO_LOCATION_MAX && role <= XPAN_ROLE_SECONDARY && status <= XPAN_STATUS_INTERNAL_ERROR)
        {
           /* Need to find the ipv6Address in list */
           for(i = 0; i < gattXpanServerInst->data.ipv6Address.numSsids; i++)
           {
               if (ssidLen == gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssidLength &&
                   CsrMemCmp(ssid, gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssid,
                               gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssidLength) == 0)
               {
                   uint8 ipv6AddressCount = gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].numIpv6Addresses;

                   /* Determine whether Ipv6 Address exists */
                   for(j = 0; j < ipv6AddressCount; j++)
                   {
                       if(CsrMemCmp(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].ipv6Address,
                                       ipv6Address, XPAN_SERVER_IPV6_ADDRESS_SIZE) == 0)
                       {
                           /* Update values if an attribute is to be changed */
                           if((gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].audioLocation != audioLocation) ||
                               gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].role != role ||
                               CsrMemCmp(macAddress, gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].macAddress,
                                                              GATT_XPAN_SERVER_MAC_ADDRESS_SIZE) ||
                               gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].status != status ||
                               bssidChanged)
                           {
                               gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].audioLocation = audioLocation;
                               gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].role = role;
                               SynMemCpyS(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].macAddress,
                                          GATT_XPAN_SERVER_MAC_ADDRESS_SIZE, macAddress, GATT_XPAN_SERVER_MAC_ADDRESS_SIZE);
                               gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].status = status;
                               bssidChanged = FALSE;

                               if (notify)
                               {
                                   xpanServerHandleChangeIpv6Address(gattXpanServerInst);
                               }

                               return TRUE;
                           }
                           else
                           {
                               /* Values are the same */
                               return FALSE;
                           }
                       }
                   }
                   /* Ipv6 address does not exist and needs to be added.
                    * Allocate more memory for pointer array. */
                   gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses = (Ipv6AddressData*) xpanPmemRealloc(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses,
                                                                                                                       ipv6AddressCount*sizeof(Ipv6AddressData),
                                                                                                                       (ipv6AddressCount+1)*sizeof(Ipv6AddressData));
                   SynMemCpyS(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[ipv6AddressCount].ipv6Address,
                              XPAN_SERVER_IPV6_ADDRESS_SIZE, ipv6Address, XPAN_SERVER_IPV6_ADDRESS_SIZE);
                   gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[ipv6AddressCount].audioLocation = audioLocation;
                   gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[ipv6AddressCount].role = role;
                   SynMemCpyS(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[ipv6AddressCount].macAddress,
                              GATT_XPAN_SERVER_MAC_ADDRESS_SIZE, macAddress, GATT_XPAN_SERVER_MAC_ADDRESS_SIZE);
                   gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[ipv6AddressCount].status = status;
                   gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].numIpv6Addresses++;

                    /* Changing the characteristic does not always notify the clients.
                     * Subscribed clients for notification of the ipv4 address characteristic are notified
                     * in the following instances:
                     * - New address added has a role of primary.
                     * - New address has a role of secondary, successfully obtained the IP Address
                     *   and primary is already in the list.
                     */
                    if (role == XPAN_ROLE_PRIMARY)
                    {
                        if (notify)
                       {
                           xpanServerHandleChangeIpv6Address(gattXpanServerInst);
                       }
                    }
                    else if (gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].numIpv6Addresses > 1 &&
                            status == XPAN_STATUS_SUCCESS)
                    {
                        uint8 k;
                        /* Need to determine whether primary exists in the list of IP Addresses */
                        for(k = 0; k < ipv6AddressCount; k++)
                        {
                            /* if the the primary exsits and was successful then we notify */
                            if(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[k].role == XPAN_ROLE_PRIMARY &&
                                gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[k].status == XPAN_STATUS_SUCCESS)
                            {
                                if (notify)
                                {
                                    xpanServerHandleChangeIpv6Address(gattXpanServerInst);
                                }
                                break;
                            }
                        }
                    }

                   return TRUE;
               }
           }
       }

       GATT_XPAN_SERVER_ERROR("XPAN: Invalid parameter.");
   }
   /* Nothing updated */
   return FALSE;
}

bool GattXpanServerAddIpv6Address(ServiceHandle srvcHandle, uint8 *ssid,
                                  uint8 ssidLen, uint8 *ipv6Address,
                                  GattXpanAudioLoc audioLocation, GattXpanRole role,
                                  uint8* macAddress,
                                  GattXpanIpAddressStatus status)
{
    return GattXpanServerAddIpv6AddressV2(srvcHandle, ssid, ssidLen, ipv6Address, audioLocation, role, macAddress, status, TRUE);
}

bool GattXpanServerRemoveIpv6Address(ServiceHandle srvcHandle, uint8 *ssid,
                                    uint8 ssidLen, uint8 *ipv6Address)
{
    uint8 i, j;
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        /* Need to find the ipv6Address in list */
        for(i = 0; i < gattXpanServerInst->data.ipv6Address.numSsids; i++)
        {
            if (ssidLen == gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssidLength &&
                !CsrMemCmp(ssid, gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssid,
                            gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssidLength))
            {
                uint8 ipv6AddressCount = gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].numIpv6Addresses;

                /* Determine whether Ipv6 Address exists */
                for(j = 0; j < ipv6AddressCount; j++)
                {
                    if(!CsrMemCmp(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j].ipv6Address,
                                    ipv6Address, XPAN_SERVER_IPV6_ADDRESS_SIZE))
                    {
                        if (j != ipv6AddressCount-1)
                        {
                            gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[j] = gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses[ipv6AddressCount-1];
                        }
                        gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses = (Ipv6AddressData *)xpanPmemRealloc(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses,
                                                                                                                            ipv6AddressCount*sizeof(Ipv6AddressData),
                                                                                                                            (ipv6AddressCount-1)*sizeof(Ipv6AddressData));
                        gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].numIpv6Addresses--;

                        xpanServerHandleChangeIpv6Address(gattXpanServerInst);

                        return TRUE;
                    }
                }
            }
        }

        GATT_XPAN_SERVER_ERROR("XPAN: Invalid parameter, ipv6 address does not exist.");
    }

    return FALSE;
}

bool GattXpanServerSetBearerPreference(ServiceHandle srvcHandle, BearerPreference preference)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        if(preference.bearerPreference <= XPAN_BEARER_PREFERENCE_MAX &&
		   preference.urgencyLevel <= XPAN_URGENCY_LEVEL_MAX)
        {
            XpanClientDataElement *client;

            gattXpanServerInst->data.bearerPreference.bearerPreference = preference.bearerPreference;
            gattXpanServerInst->data.bearerPreference.urgencyLevel     = preference.urgencyLevel;

            for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
            {
                if(client->clientData.clientCfg.bearerPreferenceClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint16 valueLength;
                    uint8  *value = xpanServerComposeBearerPreferenceValue(&valueLength, gattXpanServerInst);

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        client->clientData.cid,
                        HANDLE_BEARER_PREFERENCE,
                        GATT_XPAN_SERVER_BEARER_PREFERENCE_SIZE,
                        value);
                }
            }

            return TRUE;
        }
    }

    return FALSE;
}

static void xpanServerSendBearerPreferenceChangedNotification(GattXpanServerData *inst, ConnectionId cid, BearerPreference preference)
{
    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*GATT_XPAN_SERVER_BEARER_PREFERENCE_SIZE);
    value[0] = preference.bearerPreference;
    value[1] = preference.urgencyLevel;

    xpanServerSendCharacteristicChangedNotification(
        inst->gattId,
        cid,
        HANDLE_BEARER_PREFERENCE,
        GATT_XPAN_SERVER_BEARER_PREFERENCE_SIZE,
        value);
}

bool GattXpanServerNotifyBearerPreference(ServiceHandle srvcHandle, ConnectionId cid, BearerPreference preference, bool notifyAll)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    XpanClientDataElement *client = NULL;

    if(!gattXpanServerInst)
        return FALSE;

    if(preference.bearerPreference > XPAN_BEARER_PREFERENCE_MAX ||
       preference.urgencyLevel > XPAN_URGENCY_LEVEL_MAX)
    {
        return FALSE;
    }

    if(!notifyAll)
    {
        client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);
        if(client && client->clientData.clientCfg.bearerPreferenceClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
        {
            xpanServerSendBearerPreferenceChangedNotification(gattXpanServerInst, cid, preference);
        }
		else
		{
			GATT_XPAN_SERVER_ERROR("GattXpanServerNotifyBearerPreference failed, client = %p, bearerPrefConfig = %d",
                                            client, (client ? client->clientData.clientCfg.bearerPreferenceClientCfg : 0xFF));
		}
    }
    else
    {
        for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
        {
            if(client->clientData.clientCfg.bearerPreferenceClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                xpanServerSendBearerPreferenceChangedNotification(gattXpanServerInst, client->clientData.cid, preference);
            }
        }
    }

    return TRUE;
}

bool GattXpanServerEnableCharacteristicControl(ServiceHandle srvcHandle, uint32 *characteristicBitMask, uint8 characteristicBitMaskSize)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(!gattXpanServerInst || !characteristicBitMask || (characteristicBitMaskSize > GATT_XPAN_CHARACTERISTICS_MASK_SIZE))
    {
        GATT_XPAN_SERVER_ERROR("GattXpanServerEnableCharacteristicControl: Invalid parameter, gattXpanServerInst = %p, characteristicBitMask = %d, characteristicBitMaskSize = %d",
                                            gattXpanServerInst, characteristicBitMask, characteristicBitMaskSize);
        return FALSE;
    }

    for(int i = 0; i < characteristicBitMaskSize; i++)
    {
        gattXpanServerInst->characteristicsBitMask[i] = characteristicBitMask[i];
    }

    CsrPmemFree(characteristicBitMask);

    return TRUE;
}

bool GattXpanServerBearerPreferenceReadRsp(ServiceHandle srvcHandle, ConnectionId cid, BearerPreference preference)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);
    uint8 *value = NULL;

    if(!gattXpanServerInst)
        return FALSE;

    if(!((gattXpanServerInst->characteristicsBitMask[0]) & GATT_XPAN_CHARACTERISTICS_MASK_BEARER_PREFERENCE))
    {
        xpanServerSendAccessErrorRsp(gattXpanServerInst->gattId, cid, HANDLE_BEARER_PREFERENCE, CSR_BT_GATT_RESULT_INTERNAL_ERROR);
        return FALSE;
    }

    value = (uint8 *)CsrPmemAlloc(GATT_XPAN_SERVER_BEARER_PREFERENCE_SIZE);
    value[0] = preference.bearerPreference;
    value[1] = preference.urgencyLevel;

    xpanServerSendAccessRsp(gattXpanServerInst->gattId, cid, HANDLE_BEARER_PREFERENCE, CSR_BT_GATT_RESULT_SUCCESS, GATT_XPAN_SERVER_BEARER_PREFERENCE_SIZE, value);

    return TRUE;
}

bool GattXpanServerSetConnectedDevices(ServiceHandle srvcHandle, ConnectedDevices *connectedDevices)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);
    uint16 valueLength = gattXpanServerInst ? sizeof(ConnectedDevicesData)*gattXpanServerInst->data.connectedDevices.numDevices : 0;
    uint8 deviceCount = 0;

    for(;deviceCount < connectedDevices->numDevices; deviceCount++)
    {
        /* Check transport is valid */
        if(connectedDevices->connectedDevice[deviceCount].transport <= XPAN_TRANSPORT_WI_FI_24G_AP)
        {
            /* CenterFrequency is invalid when transport is XPAN_TRANSPORT_BLUETOOTH_TWM or XPAN_TRANSPORT_BLUETOOTH_LE_AUDIO */
            if(((connectedDevices->connectedDevice[deviceCount].transport == XPAN_TRANSPORT_BLUETOOTH_TWM) ||
                (connectedDevices->connectedDevice[deviceCount].transport == XPAN_TRANSPORT_BLUETOOTH_LE_AUDIO)) &&
                (connectedDevices->connectedDevice[deviceCount].primaryFrequency != 0))
            {
                return FALSE;
            }
        }
        else
        {
            return FALSE;
        }
    }

    /* The characteristic can be changed with the following conditions:
     * - The service handle must return instance data.
     * - either the values for the connected devices has changed or the number of devices has changed.
     */
    if(gattXpanServerInst &&
            ((CsrMemCmp(connectedDevices->connectedDevice, gattXpanServerInst->data.connectedDevices.connectedDevice, valueLength) != 0) ||
            connectedDevices->numDevices != gattXpanServerInst->data.connectedDevices.numDevices))
    {
        uint8 *value = NULL;
        XpanClientDataElement *client;

        gattXpanServerInst->data.connectedDevices.numDevices = connectedDevices->numDevices;
        pfree(gattXpanServerInst->data.connectedDevices.connectedDevice);
        gattXpanServerInst->data.connectedDevices.connectedDevice = (ConnectedDevicesData *)CsrPmemAlloc(sizeof(ConnectedDevicesData)*connectedDevices->numDevices);
        SynMemCpyS(gattXpanServerInst->data.connectedDevices.connectedDevice, sizeof(ConnectedDevicesData)*connectedDevices->numDevices,
                    connectedDevices->connectedDevice, sizeof(ConnectedDevicesData)*connectedDevices->numDevices);

        valueLength = (XPAN_SERVER_CONNECTED_DEVICES_DATA_SIZE*gattXpanServerInst->data.connectedDevices.numDevices) + 1;

        for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
        {
            if(client->clientData.clientCfg.connectedDevicesClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                value = xpanServerComposeConnectedDevicesValue(&valueLength, gattXpanServerInst);

                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    client->clientData.cid,
                    HANDLE_CONNECTED_DEVICES,
                    valueLength,
                    value);
            }
        }

        return TRUE;
    }

    return FALSE;
}

static void xpanServerSendRequestedSapPowerStateChangedNotification(GattXpanServerData *inst, ConnectionId cid, RequestedSapPowerState state)
{
    uint8 *value = (uint8 *)CsrPmemAlloc(sizeof(uint8)*GATT_XPAN_SERVER_REQUESTED_SAP_POWER_STATE_SIZE);
    value[0] = state;

    xpanServerSendCharacteristicChangedNotification(
        inst->gattId,
        cid,
        HANDLE_REQUESTED_SAP_POWER_STATE,
        GATT_XPAN_SERVER_REQUESTED_SAP_POWER_STATE_SIZE,
        value);
}

bool GattXpanServerSetRequestedSapPowerState(ServiceHandle srvcHandle, RequestedSapPowerState state)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        if(state <= XPAN_SAP_POWER_STATE_ACTIVE)
        {
            XpanClientDataElement *client;

            gattXpanServerInst->data.requestedSapPowerState = state;

            for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
            {
                if(client->clientData.clientCfg.requestedSapPowerStateCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    xpanServerSendRequestedSapPowerStateChangedNotification(gattXpanServerInst, client->clientData.cid, state);
                }
            }

            return TRUE;
        }
    }

    return FALSE;
}

bool GattXpanServerNotifyRequestedSapPowerState(ServiceHandle srvcHandle, ConnectionId cid, RequestedSapPowerState state, bool notifyAll)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    XpanClientDataElement *client = NULL;

    if(!gattXpanServerInst)
        return FALSE;

    if(state > XPAN_SAP_POWER_STATE_ACTIVE)
    {
        GATT_XPAN_SERVER_ERROR("GattXpanServerNotifyRequestedSapPowerState: Invalid power state");
        return FALSE;
    }

    if(!notifyAll)
    {
        client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);
        if(client && client->clientData.clientCfg.requestedSapPowerStateCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
        {
            xpanServerSendRequestedSapPowerStateChangedNotification(gattXpanServerInst, cid, state);
        }
        else
        {
            GATT_XPAN_SERVER_ERROR("GattXpanServerNotifyRequestedSapPowerState failed, client = %p, requestedSapPowerStateCfg = %d",
                                            client, (client ? client->clientData.clientCfg.requestedSapPowerStateCfg : 0xFF));
        }
    }
    else
    {
        for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
        {
            if(client->clientData.clientCfg.requestedSapPowerStateCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                xpanServerSendRequestedSapPowerStateChangedNotification(gattXpanServerInst, client->clientData.cid, state);
            }
        }
    }

    return TRUE;
}

bool GattXpanServerSetMacAddress(ServiceHandle srvcHandle, const MacAddress *macAddress)
{
    uint8 macAddressIndex;
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);
    uint8 numMacAddresses;
    XpanClientDataElement *client;
    uint8 *value = NULL;
    uint16 valueLength = 0;

    if(gattXpanServerInst)
    {
        numMacAddresses = MIN(macAddress->numMacAddresses, gattXpanServerInst->data.macAddress.numMacAddresses);
        /* Ensure audio location values are valid */
        for(macAddressIndex = 0; macAddressIndex < macAddress->numMacAddresses; macAddressIndex++)
        {
            if(macAddress->macAddressData[macAddressIndex].audioLocation > GATT_XPAN_SERVER_AUDIO_LOCATION_MAX)
                return FALSE;
        }

        if(macAddress->numMacAddresses == gattXpanServerInst->data.macAddress.numMacAddresses)
        {
            bool matchingAddresses = TRUE;
            for(macAddressIndex = 0; macAddressIndex < numMacAddresses; macAddressIndex++)
            {
                if(CsrMemCmp(macAddress->macAddressData[macAddressIndex].macAddress,
                             gattXpanServerInst->data.macAddress.macAddressData[macAddressIndex].macAddress,
                             GATT_XPAN_SERVER_MAC_ADDRESS_SIZE) != 0)
                    matchingAddresses = FALSE;
            }
            /* Same number of addresses that match therefore no difference */
            if(matchingAddresses == TRUE)
                return FALSE;
        }

        /* Valid data has been passed */
        gattXpanServerInst->data.macAddress.numMacAddresses= macAddress->numMacAddresses;
        pfree(gattXpanServerInst->data.macAddress.macAddressData);
        gattXpanServerInst->data.macAddress.macAddressData = (MacAddressData *)CsrPmemAlloc(sizeof(MacAddressData)*macAddress->numMacAddresses);

        for(macAddressIndex = 0; macAddressIndex < macAddress->numMacAddresses; macAddressIndex++)
        {
            SynMemCpyS(gattXpanServerInst->data.macAddress.macAddressData[macAddressIndex].macAddress, GATT_XPAN_SERVER_MAC_ADDRESS_SIZE,
                        macAddress->macAddressData[macAddressIndex].macAddress, GATT_XPAN_SERVER_MAC_ADDRESS_SIZE);
            gattXpanServerInst->data.macAddress.macAddressData[macAddressIndex].audioLocation = macAddress->macAddressData[macAddressIndex].audioLocation;
        }

        for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
        {
            if(client->clientData.clientCfg.macAddressesClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                value = xpanServerComposeMacAddressValue(&valueLength, gattXpanServerInst);

                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    client->clientData.cid,
                    HANDLE_MAC_ADDRESSES,
                    valueLength,
                    value);
            }
        }

        return TRUE;
    }

    return FALSE;
}

bool GattXpanServerSetServerFeatures(ServiceHandle srvcHandle, const XpanServerFeatures serverFeatures)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *)ServiceHandleGetInstanceData(srvcHandle);

    if (gattXpanServerInst &&
        XPAN_SERVER_CHECK_FEATURES_BITS_VALID(serverFeatures) &&
        (CsrMemCmp(serverFeatures, gattXpanServerInst->data.features, XPAN_SERVER_SERVER_FEATURES_SIZE) != 0))
    {
        XpanClientDataElement *client;
        uint8 *value =NULL;

        SynMemCpyS(gattXpanServerInst->data.features, XPAN_SERVER_SERVER_FEATURES_SIZE, serverFeatures, XPAN_SERVER_SERVER_FEATURES_SIZE);

        for (client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
        {
            if (client->clientData.clientCfg.serverFeaturesCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                value = (uint8 *)CsrPmemAlloc(sizeof(uint8) * XPAN_SERVER_SERVER_FEATURES_SIZE);
                SynMemCpyS(value, XPAN_SERVER_SERVER_FEATURES_SIZE, gattXpanServerInst->data.features, XPAN_SERVER_SERVER_FEATURES_SIZE);

                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    client->clientData.cid,
                    HANDLE_SERVER_FEATURES,
                    XPAN_SERVER_SERVER_FEATURES_SIZE,
                    value);
            }
        }

        return TRUE;
    }

    return FALSE;
}

bool GattXpanServerSetAudioBearerSwitch(ServiceHandle srvcHandle, const XpanAudioBearerSwitchResponse response, ConnectionId cid)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);
        if (client)
        {
            if(XPAN_SERVER_CHECK_BITS_VALID(response, XPAN_SERVER_AUDIO_BEARER_SWITCH_RESPONSE_VALID_BITS))
            {
                client->clientData.audioBearerStatus = response;

                if (client->clientData.clientCfg.audioBearerSwitchResponseCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemZalloc(sizeof(uint8)*XPAN_SERVER_AUDIO_BEARER_SWITCH_RESPONSE_SIZE);
                    value[0] = client->clientData.audioBearerStatus;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_AUDIO_BEARER_SWITCH_RESPONSE,
                        XPAN_SERVER_AUDIO_BEARER_SWITCH_RESPONSE_SIZE,
                        value);
                }

                return TRUE;
            }
        }
    }

    return FALSE;
}

bool GattXpanServerSetNumDevicesPresent(ServiceHandle srvcHandle, const PresentDevices numDevices)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        if(numDevices >= XPAN_SERVER_NUM_DEVICES_PRESENT_MIN &&
            gattXpanServerInst->data.numDevices != numDevices)
        {
            XpanClientDataElement *client;
            uint8 *value = NULL;

            gattXpanServerInst->data.numDevices = numDevices;

            for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
            {
                if(client->clientData.clientCfg.numDevicesPresentClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    value = (uint8 *)CsrPmemZalloc(sizeof(uint8)*XPAN_SERVER_NUM_DEVICES_PRESENT_SIZE);
                    value[0] = gattXpanServerInst->data.numDevices;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        client->clientData.cid,
                        HANDLE_NUM_DEVICES_PRESENT,
                        XPAN_SERVER_NUM_DEVICES_PRESENT_SIZE,
                        value);
                }
            }

            return TRUE;
        }
    }

    return FALSE;
}

bool GattXpanServerSetVoiceBackChannelPeriodicity(ServiceHandle srvcHandle, const VoiceBackChannelPeriodicity periodicity)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        if(periodicity >= XPAN_SERVER_PERIODICITY_MIN &&
            gattXpanServerInst->data.periodicity != periodicity)
        {
            XpanClientDataElement *client;
            uint8 *value = NULL;

            gattXpanServerInst->data.periodicity = periodicity;

            for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
            {
                if(client->clientData.clientCfg.voiceBackChannelPeriodicityClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    value = (uint8 *)CsrPmemZalloc(sizeof(uint8)*XPAN_SERVER_VOICE_CHANNEL_PERIODICITY_SIZE);
                    value[0] = gattXpanServerInst->data.periodicity;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        client->clientData.cid,
                        HANDLE_VOICE_BACK_CHANNEL_PERIODICITY,
                        XPAN_SERVER_VOICE_CHANNEL_PERIODICITY_SIZE,
                        value);
                }
            }

            return TRUE;
        }
    }

    return FALSE;
}

bool GattXpanServerSetTwtStatus(ServiceHandle srvcHandle, const TwtStatus twtStatus)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        if (twtStatus != XPAN_AUDIO_LOCATION_INVALID &&
            gattXpanServerInst->data.twtStatus != twtStatus)
        {
            XpanClientDataElement *client;
            gattXpanServerInst->data.twtStatus = twtStatus;

            GATT_XPAN_SERVER_INFO("GattXpanServerSetTwtStatus %d", gattXpanServerInst->data.twtStatus);

            for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
            {
                if(client->clientData.clientCfg.twtStatusClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = NULL;
                    value = (uint8 *)CsrPmemZalloc(sizeof(uint8)*XPAN_SERVER_TWT_STATUS_SIZE);

                    value[0] = gattXpanServerInst->data.twtStatus;
                    value[1] = gattXpanServerInst->data.twtStatus >> 8;
                    value[2] = gattXpanServerInst->data.twtStatus >> 16;
                    value[3] = gattXpanServerInst->data.twtStatus >> 24;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        client->clientData.cid,
                        HANDLE_TWT_STATUS,
                        XPAN_SERVER_TWT_STATUS_SIZE,
                        value);
                    GATT_XPAN_SERVER_INFO("GattXpanServerSetTwtStatus Notified client cid %d", client->clientData.cid);
                }
            }
            return TRUE;
        }
    }
    GATT_XPAN_SERVER_ERROR("GattXpanServerSetTwtStatus Failed");
    return FALSE;
}

bool GattXpanServerSetSapConnectionStatus(ServiceHandle srvcHandle, const SapConnectionStatus sapConnectionStatus)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        if (sapConnectionStatus != XPAN_AUDIO_LOCATION_INVALID &&
            gattXpanServerInst->data.sapConnectionStatus != sapConnectionStatus)
        {
            XpanClientDataElement *client;
            gattXpanServerInst->data.sapConnectionStatus = sapConnectionStatus;

            GATT_XPAN_SERVER_INFO("GattXpanServerSetSapConnectionStatus %d", gattXpanServerInst->data.sapConnectionStatus);

            for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.first; client; client = client->next)
            {
                if(client->clientData.clientCfg.sapConnectionStatusClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = NULL;
                    value = (uint8 *)CsrPmemZalloc(sizeof(uint8)*XPAN_SERVER_SAP_CONNECTION_STATUS_SIZE);

                    value[0] = gattXpanServerInst->data.sapConnectionStatus;
                    value[1] = gattXpanServerInst->data.sapConnectionStatus >> 8;
                    value[2] = gattXpanServerInst->data.sapConnectionStatus >> 16;
                    value[3] = gattXpanServerInst->data.sapConnectionStatus >> 24;

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        client->clientData.cid,
                        HANDLE_SAP_CONNECTION_STATUS,
                        XPAN_SERVER_SAP_CONNECTION_STATUS_SIZE,
                        value);
                    GATT_XPAN_SERVER_INFO("GattXpanServerSetSapConnectionStatus Notified client cid %d", client->clientData.cid);
                }
            }
            return TRUE;
        }
    }
    GATT_XPAN_SERVER_ERROR("GattXpanServerSetSapConnectionStatus Failed");
    return FALSE;
}


StoredSsid *GattXpanGetStoredSsid(ServiceHandle srvcHandle)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    StoredSsid *storedSsid = gattXpanServerInst ? &(gattXpanServerInst->data.storedSsid) : NULL;

    return storedSsid;
}

Ipv4Address *GattXpanGetIpv4Addresses(ServiceHandle srvcHandle)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    Ipv4Address *ipv4Address = gattXpanServerInst ? &(gattXpanServerInst->data.ipv4Address) : NULL;

    return ipv4Address;
}

Ipv6Address *GattXpanGetIpv6Addresses(ServiceHandle srvcHandle)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    Ipv6Address *ipv6Address = gattXpanServerInst ? &(gattXpanServerInst->data.ipv6Address) : NULL;

    return ipv6Address;
}

bool GattXpanServerSetRequestWiFiScanResults(ServiceHandle srvcHandle, ConnectionId cid)
{
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);

        if (client)
        {
            if (client->clientData.clientCfg.requestWiFiScanResultsClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    cid,
                    HANDLE_REQUEST_WIFI_SCAN_RESULTS,
                    XPAN_SERVER_REQUEST_WIFI_SCAN_RESULTS_SIZE,
                    NULL);

                return TRUE;
            }
        }
    }

    return FALSE;
}

bool GattXpanServerNotifyRoamingRequestResponse(ServiceHandle srvcHandle, ConnectionId cid, XpanRoamingRequestStatus status)
{
    if (status <= GATT_XPAN_ROAMING_REQUEST_REJECTED)
    {
        GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

        if(gattXpanServerInst)
        {
            XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);

            if (client)
            {
                if (client->clientData.clientCfg.roamingRequestResponseClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
                {
                    uint8 *value = (uint8 *)CsrPmemZalloc(sizeof(uint8)*XPAN_SERVER_ROAMING_REQUEST_RESPONSE_SIZE);
                    value[0] = status;
                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_ROAMING_REQUEST_RESPONSE,
                        XPAN_SERVER_ROAMING_REQUEST_RESPONSE_SIZE,
                        value);
                }

                return TRUE;
            }
        }
    }

    return FALSE;
}

bool GattXpanServerNotifyWiFiScanResults(ServiceHandle srvcHandle, ConnectionId cid, WiFiScanResults* wiFiScanResults)
{

    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);

        if (client)
        {
            if (client->clientData.clientCfg.wiFiScanResultsClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                uint16 curIndex = 0;
                int i, j;
                uint16 sizeValue = XPAN_WIFI_SCAN_TIMESTAMP_LENGTH + 1;

                for (i = 0; i < wiFiScanResults->numBssId; i++)
                {
                    /* Here 5 bytes are split as below
                       1 byte for SSID length, 2 bytes for channel, 1 byte for security mode and 1 byte for rssi */
                    sizeValue += 5 + wiFiScanResults->bssIdData[i].connectSsid.ssidLength + GATT_XPAN_SERVER_BSSID_SIZE;
                }
                uint8 *value = (uint8 *)CsrPmemZalloc(sizeValue * sizeof(uint8));

                for (i = 0; i < XPAN_WIFI_SCAN_TIMESTAMP_LENGTH; i++)
                {
                    value[curIndex++] = wiFiScanResults->timestamp[i];
                }
                value[curIndex++] = wiFiScanResults->numBssId;

                for (i = 0; i < wiFiScanResults->numBssId; i++)
                {
                    value[curIndex++] = wiFiScanResults->bssIdData[i].connectSsid.ssidLength;
                    for (j = 0; j < wiFiScanResults->bssIdData[i].connectSsid.ssidLength; j++)
                    {
                        value[curIndex++] = wiFiScanResults->bssIdData[i].connectSsid.ssid[j];
                    }

                    for (j = 0; j < GATT_XPAN_SERVER_BSSID_SIZE; j++)
                    {
                        value[curIndex++] = wiFiScanResults->bssIdData[i].bssid[j];
                    }

                    value[curIndex++] = (uint8)(wiFiScanResults->bssIdData[i].channel & 0xff);
                    value[curIndex++] = (uint8)((wiFiScanResults->bssIdData[i].channel >> 8) & 0xff);

                    value[curIndex++] = wiFiScanResults->bssIdData[i].securityMode;
                    value[curIndex++] = wiFiScanResults->bssIdData[i].rssiOfAp;
                }

                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    cid,
                    HANDLE_WIFI_SCAN_RESULTS,
                    sizeValue,
                    value);
            }

            return TRUE;
        }
    }

    return FALSE;
}

bool GattXpanServerNotifyPreferredChannels(ServiceHandle srvcHandle, ConnectionId cid, PreferredChannels* prefChannels)
{

    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);

        if (client)
        {
            if (client->clientData.clientCfg.preferredChannelsClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                uint16 sizeValue = sizeof(uint8) + (prefChannels->numChannels * sizeof(uint16));
                uint8 *value = (uint8 *)CsrPmemZalloc(sizeValue);
                value[0] = prefChannels->numChannels;

                for(int i = 0, j =1; i < prefChannels->numChannels; i++)
                {
                    value[j++] = (uint8)(prefChannels->centerFrequency[i] & 0xff);
                    value[j++] = (uint8)((prefChannels->centerFrequency[i] >> 8) & 0xff);
                }

                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    cid,
                    HANDLE_PREFERRED_CHANNELS,
                    sizeValue,
                    value);
            }

            return TRUE;
        }
    }

    return FALSE;
}

bool GattXpanServerNotifyWiFiChannelSwitchRequest(ServiceHandle srvcHandle, ConnectionId cid, XpanWiFiChannelSwitchRequestPriFreq priFreq)
{

    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);

        if (client)
        {
            if (client->clientData.clientCfg.wiFiChannelSwitchRequestClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                uint8 *value = (uint8 *)CsrPmemZalloc(sizeof(uint8)* XPAN_SERVER_WIFI_CHANNEL_SWITCH_REQUEST_SIZE);

                value[0] = (uint8)(priFreq & 0xff);
                value[1] = (uint8)((priFreq >> 8) & 0xff);

                xpanServerSendCharacteristicChangedNotification(
                    gattXpanServerInst->gattId,
                    cid,
                    HANDLE_WIFI_CHANNEL_SWITCH_REQUEST,
                    XPAN_SERVER_WIFI_CHANNEL_SWITCH_REQUEST_SIZE,
                    value);
            }

            return TRUE;
        }
    }

    return FALSE;
}

bool GattXpanServerNotifySupportedWiFiChannels(ServiceHandle srvcHandle, ConnectionId cid, SupportedWiFiChannels* supportedWiFiChannels)
{

    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if(gattXpanServerInst)
    {
        XpanClientDataElement *client = xpanFindClient(&(gattXpanServerInst->data.connectedClients), cid);

        if (client)
        {
            if (client->clientData.clientCfg.supportedWiFiChannelsClientCfg == GATT_XPAN_SERVER_CCC_NOTIFY)
            {
                /* Check if valid no of channels is present */
                if (supportedWiFiChannels && supportedWiFiChannels->numChannels > 0)
                {
                    uint16 sizeValue = sizeof(uint8) + (supportedWiFiChannels->numChannels * sizeof(uint16));
                    uint8 *value = (uint8 *)CsrPmemZalloc(sizeValue);
                    value[0] = supportedWiFiChannels->numChannels;

                    for(int i = 0, j =1; i < supportedWiFiChannels->numChannels; i++)
                    {
                        value[j++] = (uint8)(supportedWiFiChannels->centerFrequency[i] & 0xff);
                        value[j++] = (uint8)((supportedWiFiChannels->centerFrequency[i] >> 8) & 0xff);
                    }

                    xpanServerSendCharacteristicChangedNotification(
                        gattXpanServerInst->gattId,
                        cid,
                        HANDLE_SUPPORTED_WIFI_CHANNELS,
                        sizeValue,
                        value);

                    return TRUE;
                }
            }
        }
    }

    return FALSE;
}

bool GattXpanServerTerminate(ServiceHandle srvcHandle)
{
    uint8 i;
    XpanClientDataElement *client, *nextClient;
    GattXpanServerData *gattXpanServerInst = (GattXpanServerData *) ServiceHandleGetInstanceData(srvcHandle);

    if (gattXpanServerInst == NULL)
    {
        GATT_XPAN_SERVER_WARNING("XPAN: GattXpanServerTerminate: gattXpanServerInst == NULL\n\n");
        return FALSE;
    }

    /* Free Stored SSID characteristic data*/
    for(i = 0; i < gattXpanServerInst->data.storedSsid.numSsid; i++)
        pfree(gattXpanServerInst->data.storedSsid.ssid[i].ssid);
    pfree(gattXpanServerInst->data.storedSsid.ssid);

    for(i = 0; i < gattXpanServerInst->data.ipv4Address.numSsids; i++)
    {
        pfree(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Ssid.ssid);
        pfree(gattXpanServerInst->data.ipv4Address.ipv4Ssids[i].ipv4Addresses);
    }
    pfree(gattXpanServerInst->data.ipv4Address.ipv4Ssids);

    for(i = 0; i < gattXpanServerInst->data.ipv6Address.numSsids; i++)
    {
        pfree(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Ssid.ssid);
        pfree(gattXpanServerInst->data.ipv6Address.ipv6Ssids[i].ipv6Addresses);
    }
    pfree(gattXpanServerInst->data.ipv6Address.ipv6Ssids);
    pfree(gattXpanServerInst->data.connectedDevices.connectedDevice);
    pfree(gattXpanServerInst->data.macAddress.macAddressData);

    /* Remove clients*/
    for(client = (XpanClientDataElement *)gattXpanServerInst->data.connectedClients.last; client; client = nextClient)
    {
        nextClient = client->prev;
        XPAN_REMOVE_CLIENT(gattXpanServerInst->data.connectedClients, client);
    }

    CsrBtGattUnregisterReqSend(gattXpanServerInst->gattId);

    return TRUE;
}

#endif /* EXCLUDE_GATT_XPAN_SERVER_MODULE */
